#include "websocket.h"
#include <stdint.h>
#include <string.h>
#include <stdio.h>
size_t
ws_handshake_request(char buffer[], char url[], size_t size){
    int wrote = 0;
    return wrote;
}
int
ws_header_get(char http[], size_t size, char field[], char buffer[]){
    if(!strcmp(field, "URI"))
        return sscanf(http, "GET %s HTTP%*s\r\n", buffer);
    char fmt[25];
    int rc = snprintf(fmt, 25, "%s: %%s\r\n", field);
    if(rc<=0 || rc>=25)return 0;
    for(char *line = memchr(http, '\n', size);
            line++ != NULL;
            line = memchr(line, '\n', size-(line-http))){
        if(sscanf(line, fmt, buffer))return 1;
    }
    return 0;
}
size_t
ws_handshake_response(char buffer[], char req_key[], size_t size){
    size_t wrote = 0;
    unsigned char accept_key[WS_SERVER_KEY_LEN + 1];
    unsigned char sha1res[20];
    unsigned char keys[WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1];
    strncpy((char *)keys, (char *)req_key, WS_CLIENT_KEY_LEN+1);
    strcat((char *)keys, WS_GUID);
    mbedtls_sha1(keys, WS_CLIENT_KEY_LEN + WS_GUID_LEN, sha1res);
    mbedtls_base64_encode(accept_key, WS_SERVER_KEY_LEN + 1,
            &wrote, sha1res, 20);
    wrote = snprintf(buffer, size,
            "HTTP/1.1 101 Switching Protocols"
            "\r\n"
            "Upgrade: websocket"
            "\r\n"
            "Connection: Upgrade"
            "\r\n"
            "Sec-Websocket-Accept: %s"
            "\r\n"
            "\r\n"
            , accept_key);
    return wrote;
}
size_t
ws_framing(char buffer[], size_t payload_len, uint32_t mask, unsigned int opcode){
    /*  Write opcode.   */
    register unsigned int wrote = 0;
    buffer[wrote++]
        = (opcode ? opcode :(WS_FIN|WS_BINARY)) & 0xFF;
#define SHF_(x,y,z) ((x)=((y)>>((z)*8))&0xFF)
    /*  Write payload length.   */
    if(payload_len < 0x7E)
        buffer[wrote++] = payload_len & 0x7F;
    else if(payload_len < UINT16_MAX){
        buffer[wrote++] = 0x7E;
        SHF_(buffer[wrote++], payload_len, 1);
        SHF_(buffer[wrote++], payload_len, 0);
    }else{
        buffer[1] = 0x7F;
        SHF_(buffer[wrote++], payload_len, 7);
        SHF_(buffer[wrote++], payload_len, 6);
        SHF_(buffer[wrote++], payload_len, 5);
        SHF_(buffer[wrote++], payload_len, 4);
        SHF_(buffer[wrote++], payload_len, 3);
        SHF_(buffer[wrote++], payload_len, 2);
        SHF_(buffer[wrote++], payload_len, 1);
        SHF_(buffer[wrote++], payload_len, 0);
    }
    /*  Write mask. */
    if(mask){
        buffer[1] |= WS_HAVE_MASK;
        SHF_(buffer[wrote++], mask, 3);
        SHF_(buffer[wrote++], mask, 2);
        SHF_(buffer[wrote++], mask, 1);
        SHF_(buffer[wrote++], mask, 0);
    }
#undef SHF_
    return (size_t)wrote;
}
size_t
ws_write(char frame[], size_t offset, char src[], size_t src_size){
    memcpy(frame + offset, src, src_size);
    if(frame[1] & WS_HAVE_MASK){
        unsigned char *mask = (unsigned char *)frame+offset-4;
        for(int n = src_size; n--;)
            frame[offset + n] ^= mask[n & 3];
    }
    return(offset + src_size);
}
size_t
ws_read(char frame[], size_t buffer_size, int *opcode){
    *opcode = (unsigned char)frame[0];
    switch(*opcode & 0x0F){
        case WS_PING:
            *opcode <<= 8;
            *opcode |= WS_PONG;
            break;
        case WS_CLOSE:
            *opcode <<= 8;
            *opcode |= WS_CLOSE;
            break;
    }
    *opcode |= WS_FIN;
    char *data = ws_frame_data(frame);
    size_t data_len = ws_frame_len(frame);
    size_t frame_len = data - frame + data_len;
    if(frame_len > buffer_size)
        goto exit;
    if(frame[1] & WS_HAVE_MASK){
        /*  Unmask. */
        unsigned char *mask = (unsigned char *)data - 4;
        for(int n = data_len; n--;)
            data[n] ^= mask[n & 3];
        frame[1] |= WS_HAVE_MASK;
    }
exit:
    return frame_len;
}
